library ieee;
use ieee.std_logic_1164.all;
use IEEE.NUMERIC_STD.all;

entity MemoryArbiter is
  port
    (
      -- clk (neg-edge triggered), active-low reset
      clk, nReset     : in  std_logic;
      -- memory signals (in order)
      memAddr         : out std_logic_vector(15 downto 0);  -- mem address being read/written
      memWriteData    : out std_logic_vector(31 downto 0);  -- mem data to be written
      memWEN          : out std_logic;  -- write enable
      memREN          : out std_logic;  -- read enable
      memReadData     : in  std_logic_vector(31 downto 0);  -- mem data to be read
      memState        : in  std_logic_vector(1 downto 0);  -- state of mem
      -- icache signals (in order)
      icacheWait      : out std_logic;  -- icache must wait if 1
      icacheREN       : in  std_logic;  -- icache read request
      icacheAddr      : in  std_logic_vector(31 downto 0);  -- icache address being read
      icacheReadData  : out std_logic_vector(31 downto 0);  -- icache data to be read
      -- dcache signals (in order)
      dcacheREN       : in  std_logic;  -- dcache read request
      dcacheWEN       : in  std_logic;  -- dcache write request
      dcacheWait      : out std_logic;  -- dcache must wait if 1
      dcacheAddr      : in  std_logic_vector(31 downto 0);  -- dcache address being read
      dcacheReadData  : out std_logic_vector(31 downto 0);  -- dcache data to be read
      dcacheWriteData : in  std_logic_vector(31 downto 0)  -- dcache data to be written
      );
end MemoryArbiter;

architecture MemoryArbiter_arch of MemoryArbiter is

  signal t_memState : std_logic_vector(1 downto 0);
  type   state is (s_idle, s_inst, s_data);

  signal currState, nextState     : state;
  signal prevReqInst, prevReqData : std_logic;
  signal instReq, dataReq         : std_logic;
begin

  instReq <= icacheREN;                 -- in wants to use mem
  dataReq <= dcacheREN or dcacheWEN;    --data wants to use mem

  nstate : process(clk, nReset)
  begin
    if(nReset = '0') then
      currState   <= s_idle;
      prevReqInst <= '0';
      prevReqData <= '0';
    elsif(rising_edge(clk)) then
      currState   <= nextState;
      prevReqInst <= instReq;
      prevReqData <= dataReq;
    end if;
  end process;


  nsl : process (currState, instReq, dataReq, prevReqInst, prevReqData, icacheREN, dcacheREN, dcacheWEN, icacheAddr, dcacheAddr, dcacheWriteData, memReadData, t_memState)

  begin  -- process nsl
    nextState <= currState;
    case currState is
      when s_idle =>                    --okay this is normal behavior.
        if(dataReq = '1' and instReq = '1') then  -- well this isn't good they
                                                  -- both want shit to do.
          if(prevReqData = '1') then
            nextState <= s_data;
          elsif(prevReqInst = '1') then
            nextState <= s_inst;
          else
            nextState <= s_data;
          end if;

        else
          --no conflict lets just assign normally.
          if(dataReq = '1') then
            nextState <= s_data;                         --start data mem stuff
          elsif(instReq = '1') then
            nextState <= s_inst;                          --start inst mem stuff
          end if;
        end if;
      when s_inst =>
        if(t_memState = "00" or t_memState = "10") then  -- ready or free
          nextState <= s_idle;
        end if;
      when s_data =>
        if(t_memState = "00" or t_memState = "10") then  -- ready or free
          nextState <= s_idle;
        end if;
      when others =>
        nextState <= currState;
    end case;
  end process nsl;

  outl : process(currState, nextState, instReq, dataReq, prevReqInst, prevReqData, icacheREN, dcacheREN, dcacheWEN, icacheAddr, dcacheAddr, dcacheWriteData, memReadData, t_memState)
  begin  -- process
    case currState is
      when s_idle =>
        case nextState is
          when s_idle =>
            dcacheWait     <= '0';
            icacheWait     <= '0';
            memWEN         <= '0';
            memRen         <= '0';
            memAddr        <= x"0000";
            memWriteData   <= x"00000000";
            icacheReadData <= x"00000000";
            dcacheReadData <= x"00000000";
          when s_inst =>
            
            dcacheWait <= dataReq;      --we less obviously may want to be
                                        --doing things with data so wait if we
                                        --are requesting
            if(t_memState = "10") then
              icacheWait <= '0';        --we are obviously doing things with
                                        --inst so wait until ready or free
            else
              icacheWait <= '1';
            end if;


            memWEN         <= '0';
            memRen         <= instReq;  -- well we want to read
            memAddr        <= icacheAddr(15 downto 0);
            memWriteData   <= x"00000000";
            icacheReadData <= memReadData;  -- pass 'er on through
            dcacheReadData <= x"00000000";
          when s_data =>
            icacheWait <= instReq;      --we less obviously may want to be
                                        --doing things with data so wait if we
                                        --are requesting
            if(t_memState = "10") then
              dcacheWait <= '0';        --we are obviously doing things with
                                        --inst so wait until ready or free
            else
              dcacheWait <= '1';
            end if;


            memWEN         <= dcacheWEN;
            memRen         <= dcacheREN;    -- well we want to read
            memAddr        <= dcacheAddr(15 downto 0);
            memWriteData   <= dcacheWriteData;
            dcacheReadData <= memReadData;  -- pass 'er on through
            icacheReadData <= x"00000000";
          when others => null;
        end case;
      when s_inst =>
        dcacheWait <= dataReq;          --we less obviously may want to be
                                        --doing things with data so wait if we
                                        --are requesting
        if(t_memState = "10") then
          icacheWait <= '0';            --we are obviously doing things with
                                        --inst so wait until ready or free
        else
          icacheWait <= '1';
        end if;


        memWEN         <= '0';
        memRen         <= instReq;      -- well we want to read
        memAddr        <= icacheAddr(15 downto 0);
        memWriteData   <= x"00000000";
        icacheReadData <= memReadData;  -- pass 'er on through
        dcacheReadData <= x"00000000";
      when s_data =>
        icacheWait <= instReq;          --we less obviously may want to be
                                        --doing things with data so wait if we
                                        --are requesting
        if(t_memState = "10") then
          dcacheWait <= '0';            --we are obviously doing things with
                                        --inst so wait until ready or free
        else
          dcacheWait <= '1';
        end if;


        memWEN         <= dcacheWEN;
        memRen         <= dcacheREN;    -- well we want to read
        memAddr        <= dcacheAddr(15 downto 0);
        memWriteData   <= dcacheWriteData;
        dcacheReadData <= memReadData;  -- pass 'er on through
        icacheReadData <= x"00000000";
      when others => null;
    end case;
    
  end process;

  t_memState <= memState;
  
end MemoryArbiter_arch;
